﻿#ifndef _CELL_STREAM_HPP_
#define _CELL_STREAM_HPP_

#include <cstdint>
#include <cstring>
#include "CELLLog.hpp"

/*
	字节流
*/
class CELLStream
{
public:
	// 内存块由数据传进来，对象析构是否释放由用户决定
	CELLStream(char* pData, int nSize, bool bDel = false) :
		_nSize(nSize), _bDelete(bDel)
	{
		_pBuff = pData;
	}

	// 内存块由对象new出来，对象析构会被释放
	CELLStream(int nSize = 1024) :
		_nSize(nSize), _bDelete(true)
	{
		_pBuff = new char[_nSize];
	}

	virtual ~CELLStream()
	{
		if (_bDelete && !_pBuff)
		{
			delete[] _pBuff;
			_pBuff = nullptr;
		}
	}

	char* data()
	{
		return _pBuff;
	}

	int length()
	{
		return _nWritePos;
	}
protected:
	// 能读n字节的数据吗
	inline bool canRead(int n)
	{
		return _nSize - _nReadPos >= n;
	}

	// 能写入n字节的数据吗
	inline bool canWrite(int n)
	{
		return _nSize -_nWritePos >= n;
	}

	// 已写入位置，添加n字节长度
	inline void push(int n)
	{
		_nWritePos += n;
	}

	void setWritePos(int n)
	{
		_nWritePos = n;
	}

	int getWritePos()
	{
		return _nWritePos;
	}

	// 已读出位置，添加n字节长度
	inline void pop(int n)
	{
		_nReadPos += n;
	}
public:
	// read
	template<typename Type>
	bool Read(Type& dst, bool offset = true)
	{
		// 计算要读取数据的长度
		auto nLen = sizeof(Type);
		// 判断能不能读
		if (canRead(nLen))
		{
			// 将要读取的数据拷贝出来
			memcpy(&dst, _pBuff + _nReadPos, nLen);
			// 更新已读位置
			if (offset)
				pop(nLen);
			return true;
		}
		// 读不到数据应该是一种错误的表现
		CELLLog_Error("CELLStream::Read failed");
		return false;
	}

	template<typename Type>
	bool onlyRead(Type& dst = 0)
	{
		return Read(dst, false);
	}

	template<typename Type>
	uint32_t ReadArray(Type* pArr, uint32_t len)
	{
		// 读取数组元素个数
		uint32_t len1 = 0;
		// 偷窥一下下数组的长度位(读取位置不会变)，防止出现读取不出来却把数组长度位丢了的结果
		Read(len1, false);
		// 传进来的数组长度能够装得下
		if (len1 <= len)
		{
			// 计算数组实际字节长度
			auto nLen = len1 * sizeof(Type);
			// 判断能不能读出
			if (canRead(nLen))
			{
				// 能读取出来，第一个表示数组长度的位置才偏移掉
				_nReadPos += sizeof(uint32_t);
				// 将要读取的数据拷贝出来
				memcpy(pArr, _pBuff + _nReadPos, nLen);
				// 更新读取位置
				pop(nLen);
				// 正常读取返回读取出来的数组长度
				return len1;
			}
		}
		CELLLog_Error("CELLStream::ReadArray failed");
		return 0;
	}

	// char
	int8_t ReadInt8(int8_t def = 0)
	{
		Read(def);
		return def;
	}

	// short
	int16_t ReadInt16(int16_t n = 0)
	{
		Read(n);
		return n;
	}

	// int
	int32_t ReadInt32(int32_t n = 0)
	{
		Read(n);
		return n;
	}

	int64_t ReadInt64(int64_t n = 0)
	{
		Read(n);
		return n;
	}

	// char
	uint8_t ReadUInt8(uint8_t def = 0)
	{
		Read(def);
		return def;
	}

	// short
	uint16_t ReadUInt16(uint16_t n = 0)
	{
		Read(n);
		return n;
	}

	// int
	uint32_t ReadUInt32(uint32_t n = 0)
	{
		Read(n);
		return n;
	}

	uint64_t ReadUInt64(uint64_t n = 0)
	{
		Read(n);
		return n;
	}

	float ReadFloat(float n = 0.0f)
	{
		Read(n);
		return n;
	}

	double ReadDouble(double n = 0.0)
	{
		Read(n);
		return n;
	}
   
	// write
	template<typename Type>
	bool Write(Type n)
	{
		// 计算要写入数据的大小
		auto nLen = sizeof(Type);
		// 判断能不能写入
		if (canWrite(nLen))
		{
			// 将要写入的数据拷贝到缓冲区尾部
			memcpy(_pBuff + _nWritePos, &n, nLen);
			// 更新写入数据尾部位置
			push(nLen);
			return true;
		}
		CELLLog_Info("CELLStream::Write failed");
		return false;
	}

	template<typename Type>
	bool WriteArray(Type* pData, uint32_t len)
	{
		// 计算写入数组大小
		auto nLen = sizeof(Type) * len;
		// 判断能不能写入
		if (canWrite(nLen + sizeof(uint32_t)))
		{
			// 将数组的第一个位置存储数组长度，读取的时候就可以知道读取数据的长度
			WriteInt32(len);
			// 将要写入的数据拷贝到缓冲区尾部
			memcpy(_pBuff + _nWritePos, pData, nLen);
			// 更新写入数据尾部位置
			push(nLen);
			return true;
		}
		CELLLog_Error("CELLStream::WriteArray failed");
		return false;
	}

	// char
	bool WriteInt8(int8_t n)
	{
		return Write(n);
	}

	// short
	bool WriteInt16(int16_t n)
	{
		return Write(n);
	}

	// int
	bool WriteInt32(int32_t n)
	{
		return Write(n);
	}

	bool WriteFloat(float n)
	{
		return Write(n);
	}

	bool WriteDouble(double n)
	{
		return Write(n);
	}
private:
	// 数据缓冲区
	char* _pBuff = nullptr;
	// 缓冲区大小
	int _nSize = 0;

	// 已写入数据尾部位置(已写入数据长度)
	int _nWritePos = 0;
	// 已读取数据尾部位置
	int _nReadPos = 0;

	// 数据块是否应该被释放
	bool _bDelete = false;
};

#endif // #ifndef _CELL_STREAM_HPP_
